#include <iostream>
#include <string.h>
#include <errno.h>
#ifndef _WIN32
#include <signal.h>
#endif // !_WIN32

#include "event.h"
#include "event2/listener.h"

#define SERVER_PORT 8000

char recv_buf[10240] = { 0 };
size_t recv_len = 0;

// 读取回调
void read_cb(struct bufferevent *bev, void *ctx) {
    std::cout << "[R]" << std::endl;

    // 读取输入缓冲数据
    char data[1024] = { 0 };
    size_t len = bufferevent_read(bev, data, sizeof(data) - 1);
    std::cout << "read[" << len << "]: " << data << std::endl;
    if (len <= 0) return;

    strncat(recv_buf, data, sizeof(recv_buf) - recv_len - 1);
    recv_len += len;
    
    if (strstr(data, "quit") != NULL) {
        std::cout << "quti" << std::endl;
        // 退出并关闭socket，因为bufferevent设置了BEV_OPT_CLOSE_ON_FREE属性
        bufferevent_free(bev);
    }

    // 发送OK到输出缓冲区
    bufferevent_write(bev, "OK", 3);   
}

// 写入回调
void write_cb(struct bufferevent *bev, void *ctx) {
    std::cout << "[W]" << std::endl;
}

// 事件回调，错误，超时，连接断开会进入
void event_cb(struct bufferevent *bev, short what, void *ctx) {
    std::cout << "[E]:" << what << std::endl;

    if ((what & BEV_EVENT_READING) && (what & BEV_EVENT_TIMEOUT)) {
        // 若是超时事件，读取数据会停止，即EV_READ不可读
        std::cout << "BEV_EVENT_READING & BEV_EVENT_TIMEOUT" << std::endl;

        // 因为设置了最低水位，此时缓存中的数据可能没有读完，需要继续读取
        char data[1024] = { 0 };
        size_t len = bufferevent_read(bev, data, sizeof(data) - 1);
        if (len > 0) {
            std::cout << "read[" << len << "]: " << data << std::endl;
            strncat(recv_buf, data, sizeof(recv_buf) - recv_len - 1);
            recv_len += len;
        }
        std::cout << "recv[" << recv_len << "]: " << recv_buf << std::endl;

        // 可以重新设置可读，一般直接断开连接
        // bufferevent_enable(bev, EV_READ);
        bufferevent_free(bev);
    }
    else if (what & BEV_EVENT_EOF) {
        // 客户端连接断开事件
        std::cout << "BEV_EVENT_EOF" << std::endl;
        bufferevent_free(bev);
    }
    else if (what & BEV_EVENT_ERROR) {
        // 错误，直接断开连接
        std::cout << "BEV_EVENT_ERROR" << std::endl;
        bufferevent_free(bev);
    }
    else {
        std::cout << "OTHERS" << std::endl;
    }
}

// 监听回调
void listen_cb(struct evconnlistener *ev, evutil_socket_t fd, struct sockaddr *sin, int slen, void *arg) {
    std::cout << "listen_cb" << std::endl;

    event_base *base = (event_base*)arg;

    // 创建 bufferevent 上下文，BEV_OPT_CLOSE_ON_FREE 清理bufferevent时关闭socket
    bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);

    // 添加监控事件
    bufferevent_enable(bev, EV_READ | EV_WRITE);

    // 设置读取水位
    bufferevent_setwatermark(bev, EV_READ,
        5,      // 低水位，默认0，即无限制，最低读取数量
        10);    // 高水位，默认0，即无限制，最高读取数量

    // 设置写入水位（目前还不太清楚具体应用，以后可能会补上）
    /*bufferevent_setwatermark(bev, EV_WRITE,
        5,      // 低水位，默认0，即无限制，缓冲区低于设置时，回调函数被调用
        0);     // 高水位无效*/

    // 设置超时时间
    timeval tv = { 3, 0 };
    bufferevent_set_timeouts(bev, &tv, NULL);

    // 设置回调函数
    bufferevent_setcb(bev, read_cb, write_cb, event_cb, base);
}

int main(int argc, char* argv[])
{
#ifdef _WIN32
    // 初始化socket库
    WSADATA wsa;
    WSAStartup(MAKEWORD(2, 2), &wsa);
#else
    // 忽略管道信号，因为发送数据给已关闭的socket会生成SIGPIPE信号，导致进程退出
    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
        return -1;
#endif

    std::cout << "libevent bufferevent test" << std::endl;
    // 创建libevent上下文
    event_base *base = event_base_new();
    if (!base) {
        std::cout << "event_base_new failed" << std::endl;
        return -1;
    }
    std::cout << "event_base_new success" << std::endl;

    // 绑定IP和端口
    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(SERVER_PORT);

    // 创建监听事件
    evconnlistener *ev = evconnlistener_new_bind(base,
        listen_cb,                                  // 回调函数
        base,                                       // 回调函数参数
        LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,  // 设置地址复用和关闭时释放资源
        10,                                         // listen back
        (sockaddr*)&sin, sizeof(sin));              // 监听地址

    // 进入事件主循环
    event_base_dispatch(base);

    if (ev) evconnlistener_free(ev);
    event_base_free(base);

#ifdef _WIN32
    WSACleanup();
#endif

    return 0;
}
